package com.schoolvisitors.service;

import com.alibaba.fastjson2.JSONArray;
import com.schoolvisitors.dto.ReservationCubicleDto;
import com.schoolvisitors.entity.*;
import com.schoolvisitors.enums.ReservationStatusEnum;
import com.schoolvisitors.enums.ReservationTypeEnum;
import com.schoolvisitors.enums.RoomTypeEnum;
import com.schoolvisitors.exception.BusinessException;
import com.schoolvisitors.repository.*;

import com.schoolvisitors.response.ResultCode;
import com.schoolvisitors.vo.TimeSlotVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Slf4j
@Service
public class CubicleService {
    private final RoomRepository roomRepository;
    private final ReservationRepository reservationRepository;
    private final TimeSlotRepository timeSlotRepository;
    private final UserService userService;
    private final SystemConfigService systemConfigService;
    private final ReservationService reservationService;
    private final AdminService adminService;
    private final AdminRepository adminRepository;
    private final AdminPermissionService adminPermissionService;
    private final WeChatService weChatService;

    public CubicleService(RoomRepository roomRepository, ReservationRepository reservationRepository, TimeSlotRepository timeSlotRepository, UserService userService, SystemConfigService systemConfigService, ReservationService reservationService, AdminService adminService, AdminRepository adminRepository, AdminPermissionService adminPermissionService, WeChatService weChatService) {
        this.roomRepository = roomRepository;
        this.reservationRepository = reservationRepository;
        this.timeSlotRepository = timeSlotRepository;
        this.userService = userService;
        this.systemConfigService = systemConfigService;
        this.reservationService = reservationService;
        this.adminService = adminService;
        this.adminRepository = adminRepository;
        this.adminPermissionService = adminPermissionService;
        this.weChatService = weChatService;
    }

    public List<Reservation> getNotReviewedReservationsByMeeting(Long reviewerId) {
        // 获取状态为未预约的预约记录
        return reservationRepository.findByTypeAndStatusAndReviewerId(ReservationTypeEnum.CUBICLE_MEETING, ReservationStatusEnum.PENDING, reviewerId);
    }

    public List<Reservation> getNotReviewedReservationsByLab(Long reviewerId) {
        // 获取状态为未预约的预约记录
        return reservationRepository.findByTypeAndStatusAndReviewerId(ReservationTypeEnum.CUBICLE_LAB, ReservationStatusEnum.PENDING, reviewerId);
    }

    public List<Reservation> getReviewedReservationsByUserId(Long userId) {
        return reservationRepository.findByUserId(userId);
    }

    // 创建预约
    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
    public void reservationCubicle(Long userId, ReservationCubicleDto dto) {
        // 检查房间是否存在
        Room room = roomRepository.findByIdForUpdate(dto.getRoomId()).orElseThrow(() -> new BusinessException(ResultCode.ROOM_NOT_FOUND));

        Long adminId = dto.getReviewerId();
        Admin admin = adminRepository.findById(adminId).orElseThrow(() -> new BusinessException(ResultCode.REVIEWER_NOT_FOUND));
        if (!adminPermissionService.hasRoomPermission(admin, room)){
            throw new BusinessException(ResultCode.REVIEWER_NOT_PERMISSION);
        }

        // 判断房间是否可被预约
        if (!room.getAvailable()){
            throw new BusinessException(ResultCode.NOT_AVAILABLE);
        }
        User user = userService.findById(userId);

        if (!user.getReservationPermission()) {
            throw new BusinessException(ResultCode.USER_NOT_RESERVATION_PERMISSION);
        }

        // 最大提前预约天数
        Long advanceReservationDay = systemConfigService.getSystemConfigToLong("advanceReservationDay");
        long daysBetween = ChronoUnit.DAYS.between(LocalDate.now(), dto.getDate());

        if (daysBetween > advanceReservationDay) {
            throw new BusinessException(ResultCode.RESERVATION_DAY_EXCEEDED);
        }

        // 检查隔间是否在范围内
        if (dto.getCubicleNumber() > room.getCubicleCount()){
            throw new BusinessException(ResultCode.CUBICLE_NOT_FOUNT);
        }

        // 检查时间段是否存在
        Optional<TimeSlot> timeSlotOptional = timeSlotRepository.findById(dto.getTimeSlotId());
        if (timeSlotOptional.isEmpty()) {
            throw new BusinessException(ResultCode.TIME_SLOT_NOT_FOUND);
        }

        // 判断是否预约过去的时间段
        TimeSlot timeSlot = timeSlotOptional.get();
        LocalDate today = LocalDate.now();
        if (dto.getDate().isBefore(today)) {
            throw new BusinessException(ResultCode.TIME_SLOT_EXPIRED);
        } else if (dto.getDate().isEqual(today)) {
            LocalTime now = LocalTime.now();
            if (timeSlot.getEndTime().isBefore(now)) {
                throw new BusinessException(ResultCode.TIME_SLOT_EXPIRED);
            }
        }

        // 检查时间段是否已经被预约
        Optional<Reservation> existingReservation = reservationRepository
                .findByRoomIdAndCubicleNumberAndTimeSlotIdAndDateAndStatus(
                        dto.getRoomId(),
                        dto.getCubicleNumber(),
                        dto.getTimeSlotId(),
                        dto.getDate(),
                        ReservationStatusEnum.APPROVED
                );

        if (existingReservation.isPresent()) {
            throw new BusinessException(ResultCode.CUBICLE_IS_RESERVATION);
        }

        Optional<Reservation> existingUserReservation = reservationRepository
                .findByRoomIdAndCubicleNumberAndTimeSlotIdAndDateAndUserIdAndStatus(
                        dto.getRoomId(),
                        dto.getCubicleNumber(),
                        dto.getTimeSlotId(),
                        dto.getDate(),
                        user.getId(),
                        ReservationStatusEnum.PENDING
                );
        if (existingUserReservation.isPresent()) {
            throw new BusinessException(ResultCode.RESERVATION_IS_PENDING);
        }
//        Optional<User> reviewerOptional = userRepository.findById(reservationCubicleDto.getReviewerId());
//        if (reviewerOptional.isEmpty()) {
//            throw new BusinessException(ResultCode.REVIEWER_NOT_FOUND);
//        }
        log.info("用户积分: {}, 所需积分: {}", user.getPoints(), room.getPointsCost() );
        // 扣除积分
        if (user.getPoints() < room.getPointsCost()){
            throw new BusinessException(ResultCode.USER_INSUFFICIENT_POINTS);
        }
        userService.subtractPoint(user.getId(), room.getPointsCost());

        Reservation reservation = new Reservation();
        if (room.getType() == RoomTypeEnum.LAB){
            reservation.setType(ReservationTypeEnum.CUBICLE_LAB);
        } else if (room.getType() == RoomTypeEnum.MEETING){
            reservation.setType(ReservationTypeEnum.CUBICLE_MEETING);
        }
        reservation.setUser(user);
        reservation.setRoom(room);
        reservation.setCubicleNumber(dto.getCubicleNumber());
        reservation.setDate(dto.getDate());
        reservation.setPoints(room.getPointsCost());
        reservation.setTimeSlot(timeSlot);
        reservation.setReviewerId(dto.getReviewerId());
        reservation.setCreateAt(LocalDateTime.now());
        reservation.setUpdateAt(LocalDateTime.now());
        reservation.setStatus(ReservationStatusEnum.PENDING);

        // 保存预约记录
        reservationRepository.save(reservation);
    }

    // 获取用户预约列表
    public Page<Reservation> getReservationsVoByUserId(ReservationTypeEnum type, User user, Pageable pageable) {
        Long userId = user.getId();
        if (type != ReservationTypeEnum.ALL) {
            return reservationRepository.findByTypeAndUserId(type, userId, pageable);
        } else {
            return reservationRepository.findByUserId(userId, pageable);
        }
    }


    // 获取预约的时间段信息
    public JSONArray getTimeSlotsVo(Long userId, Long roomId, Short cubicleNumber, LocalDate date) {
        Room room = roomRepository.findById(roomId).orElseThrow(() -> new BusinessException(ResultCode.ROOM_NOT_FOUND));

        // 检查隔间是否在范围内
        if (cubicleNumber > room.getCubicleCount()){
            throw new BusinessException(ResultCode.CUBICLE_NOT_FOUNT);
        }
        List<TimeSlot> timeSlots = timeSlotRepository.findAllByDeleteFalseOrderByStartTimeAsc();
        List<ReservationTypeEnum> types = Arrays.asList(
                ReservationTypeEnum.CUBICLE_LAB,
                ReservationTypeEnum.CUBICLE_MEETING
        );
        List<Reservation> cubicleReservations = reservationRepository.findByTypeInAndRoomIdAndCubicleNumberAndDate(types, roomId, cubicleNumber, date);

        return TimeSlotVo.reservationToVos(userId, timeSlots, cubicleReservations);
    }

    // 取消预约
    @Transactional(rollbackFor = Exception.class)
    public void cancelCubicleReservation(Long userId, Long reservationId) {
        Reservation reservation = reservationRepository.findByIdForLock(reservationId).orElseThrow(() -> new BusinessException(ResultCode.RESERVATION_NOT_FOUND));

        if (reservation.getStatus() == ReservationStatusEnum.REJECTED) {
            throw new BusinessException(ResultCode.RESERVATION_IS_REJECTED);
        }
        if (reservation.getStatus() == ReservationStatusEnum.CANCELLED) {
            //throw new BusinessException(ResultCode.RESERVATION_NOT_FOUND);
            return;
        }
        TimeSlot timeSlot = timeSlotRepository.findById(reservation.getTimeSlot().getId()).orElseThrow(() -> new BusinessException(ResultCode.TIME_SLOT_NOT_FOUND));

        // 获取预约日期和时间合成完整的预约开始时间
        LocalDateTime reservationStartDateTime = LocalDateTime.of(reservation.getDate(), timeSlot.getStartTime());
        LocalDateTime now = LocalDateTime.now();

        // 已通过状态才判断取消规则
        if (reservation.getStatus() == ReservationStatusEnum.APPROVED) {
            // 已超过预约开始时间，不允许取消
            if (now.isAfter(reservationStartDateTime)) {
                throw new BusinessException(ResultCode.CANNOT_CANCEL_AFTER_START);
            }
            // 取消预约需要提前两个小时
            if (now.isAfter(reservationStartDateTime.minusHours(2))) {
                // 不足两个小时, 不返还积分
                log.info("取消预约超时");
                reservation.setStatus(ReservationStatusEnum.CANCELLED);
                reservationRepository.save(reservation);
                throw new BusinessException(ResultCode.CANCEL_RESERVATION_TIMEOUT);
            }
        }

        // 返还积分
        log.info("用户: {}, 添加积分: {}", userId, reservation.getPoints());
        userService.addPoint(userId, reservation.getPoints());

        reservation.setStatus(ReservationStatusEnum.CANCELLED);
        reservationRepository.save(reservation);
    }



}
